package main

import (
	"fmt"
	"github.com/go-redis/redis"
	"log"
	"time"
)

func main() {
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password set
		DB:       0,  // use default DB
	})
	defer func() {
		err := client.Close()
		fmt.Println("close client err: ", err)
	}()
	pong, err := client.Ping().Result()
	fmt.Println(pong, err)
	//redisTest(*client)
	//redisKey(*client)
	//redisString(*client)
	//redisHash(*client)
	//redisList(*client)
	//redisSet(*client)
	//redisSorttedSet(*client)

	for {
		t1 := time.Now().Unix()
		GetUserSessionNew(*client, "239700003")
		t2 := time.Now().Unix()
		log.Fatal("GetUserSessionNew花费的时间为：", t2-t1)

		time.Sleep(time.Second * 1)
	}

}

// redis Test操作
func redisTest(cli redis.Client) {
	err := cli.Set("key", "value", 0).Err()
	if err != nil {
		panic(err)
	}

	val, err := cli.Get("key").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println("key", val)

	val2, err := cli.Get("key2").Result()
	if err == redis.Nil {
		fmt.Println("key2 does not exists")
	} else if err != nil {
		panic(err)
	} else {
		fmt.Println("key2", val2)
	}
}

// redis key操作
func redisKey(cli redis.Client) {
	key := "MOD:TEN:HASH"
	expireTime := 3600
	// 该命令用于在 key 存在时删除 key
	cli.Del(key)
	// 序列化给定 key ，并返回被序列化的值。
	cli.Dump(key)
	// 检查给定 key 是否存在
	cli.Exists(key)
	// 为给定 key 设置过期时间，以秒计。
	cli.Expire(key, time.Second*time.Duration(expireTime))
	// EXPIREAT 的作用和 EXPIRE 类似，都用于为 key 设置过期时间。 不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳
	cli.ExpireAt(key, time.Now())
	// 设置 key 的过期时间以毫秒计。
	cli.PExpire(key, 1000)
	// 设置 key 过期时间的时间戳(unix timestamp) 以毫秒计
	cli.ExpireAt(key, time.Now())
	// 查找所有符合给定模式( pattern)的 key
	cli.Keys(key)
	// 将当前数据库的 key 移动到给定的数据库 db 当中。
	cli.Move(key, 1)
	// 移除 key 的过期时间，key 将持久保持。
	cli.Persist(key)
	// 以毫秒为单位返回 key 的剩余的过期时间。
	cli.PTTL(key)
	// 从当前数据库中随机返回一个 key
	cli.RandomKey()
	// 修改 key 的名称
	cli.Rename(key, "MOD:TET:HASH")
	// 仅当 newkey 不存在时，将 key 改名为 newkey
	cli.RenameNX(key, "MOD:TET:HASH")
	// 迭代数据库中的数据库键。
	cli.Scan(0, "*", 1000)
	// 返回 key 所储存的值的类型。
	cli.Type(key)
}

// redis string操作
func redisString(cli redis.Client) {
	key := "MOD:TEN:HASH"
	val := "hello"
	expireTime := 3600
	cli.Set(key, val, time.Second*time.Duration(expireTime))   //存储一个key，设置过期时间
	cli.SetNX(key, val, time.Second*time.Duration(expireTime)) // 没有这个key就存储，key存在就不用存储
	cli.PExpire(key, time.Second*time.Duration(expireTime))    // 给存在的key设置生存时间
	cli.Get(key)                                               // 获取key的值
	cli.GetSet(key, val)                                       // 将键key的值设为value,并返回键key在被设置之前的旧值,没有就返回空
	cli.Exists(key)                                            // 判断key是否存在
	cli.StrLen(key)                                            // 返回字符串值的长度,当键 key 不存在时, 命令返回 0,当 key 储存的不是字符串值时， 返回一个错误
	cli.Append(key, val)                                       // 如果键 key 已经存在并且它的值是一个字符串， APPEND 命令将把 value 追加到键 key 现有值的末尾。
	// 如果 key 不存在， APPEND 就简单地将键 key 的值设为 value ， 就像执行 SET key value 一样。
	cli.GetRange(key, 0, 4)   //  返回val索引0-4的字符，包括4。
	cli.GetRange(key, -3, -1) // 返回val最后的三个字符串
	cli.GetRange(key, 0, -1)  // 返回从val第一个到最后一个

	cli.Incr(key)              // 返回键 key 在执行+1操作之后的值,本操作的值限制在 64 位(bit)有符号数字表示之内
	cli.IncrBy(key, 10)        // 返回在加上增量 increment 之后， 键 key 当前的值
	cli.IncrByFloat(key, 2.56) // 返回在加上增量 10 之后， 键 key 的值
	cli.Decr(key)              // 对储存数字值的键 key 执行-1操作
	cli.DecrBy(key, 10)        // DECRBY 命令会返回键在执行减法10操作之后的值
	valMap := make(map[string]string)
	valMap["date"] = "2012.3.30"
	valMap["time"] = "11:00 a.m"
	valMap["weather"] = "sunny"
	cli.MSet(valMap)                    // 同时存储多个建
	cli.MSetNX(valMap)                  // 对不存在的键保存
	cli.MGet("data", "time", "weather") // 命令将返回一个列表，列表中包含了所有给定键的值
	cli.Scan(0, "*", 1000)              //

}

// redis hash操作
func redisHash(cli redis.Client) {
	key := "MOD:TEN:HASH"
	field := "name"
	val := "james"
	cli.HSet(key, field, val)           // 将哈希表 hash 中域 field 的值设置为 value
	cli.HSetNX(key, field, val)         // 如果给定域已经存在于哈希表当中， 那么命令将放弃执行设置操作。
	cli.HGet(key, field)                // 返回给定域的值, 不存在， 那么命令返回 nil 。
	cli.HExists(key, field)             //  命令在给定域存在时返回 1 ， 在给定域不存在时返回 0 。
	cli.HDel(key, field)                // 除哈希表 key 中的一个或多个指定域，不存在的域将被忽略
	cli.HLen(key)                       // 哈希表中域的数量, 当 key 不存在时，返回 0
	cli.HIncrBy(key, field, -10)        // 返回key 中域 field 的值加-10之后的值
	cli.HIncrByFloat(key, field, -1.32) // 返回key 中域 field 的值加-10之后的值
	valMap := make(map[string]interface{})
	valMap["date"] = "2012.3.30"
	valMap["time"] = "11:00 a.m"
	valMap["weather"] = "sunny"
	cli.HMSet(key, valMap)                    // 同时将多个 field-value (域-值)对设置到哈希表 key 中
	cli.HMGet(key, "date", "time", "weather") // 返回哈希表 key 中，一个或多个给定域的值
	cli.HKeys(key)                            // 获取key的所有fields
	cli.HLen(key)
	cli.HVals(key)               // 获取key的所有val
	cli.HScan(key, 0, "*", 1000) //

}

// redis list操作
func redisList(cli redis.Client) {
	key := "MOD:TEN:HASH"
	key1 := "MOD:TET:HASH"
	timeOut := 10
	// 移出并获取列表的第一个元素， 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
	cli.BLPop(time.Second*time.Duration(timeOut), key)
	// 移出并获取列表的最后一个元素， 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
	cli.BRPop(time.Second*time.Duration(timeOut), key)
	// 从列表中弹出一个值，将弹出的元素插入到另外一个列表中并返回它；
	// 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
	cli.BRPopLPush(key, key1, time.Second*time.Duration(timeOut))
	// 通过索引获取列表中的元素
	cli.LIndex(key, 10)
	// 在列表的元素前或者后插入元素
	cli.LInsert(key, "name", "age", "19")
	// 获取列表长度
	cli.LLen(key)
	// 移出并获取列表的第一个元素
	cli.LPop(key)
	// 将一个或多个值插入到列表头部
	cli.LPush(key, "name", "age")
	// 获取列表指定范围内的元素
	cli.LRange(key, 0, 10)
	// 移除列表元素
	cli.LRem(key, 0, "name")
	// 通过索引设置列表元素的值
	cli.LSet(key, 5, "hello")
	// 对一个列表进行修剪(trim)，就是说，让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删除。
	cli.LTrim(key, 0, 10)
	// 移除列表的最后一个元素，返回值为移除的元素。
	cli.RPop(key)
	// 移除列表的最后一个元素，并将该元素添加到另一个列表并返回
	cli.RPopLPush(key, "")
	// 在列表中添加一个或多个值
	cli.RPush(key, "name")
	// 为已存在的列表添加值
	cli.RPushX(key, "name")
	cli.HScan(key, 0, "*", 100)

}

// redis pub/sub发布订阅
func redisPubSub(cli redis.Client) {

	subject := "news"
	subject1 := "news1"
	// 订阅一个或多个符合给定模式的频道。
	cli.PSubscribe(subject, subject1)
	// 订阅给定的一个或多个频道的信息。
	cli.Subscribe(subject, subject1)

	// 将信息发送到指定的频道。
	data := "hello world"
	cli.Publish(subject, data)

	cli.PubSubChannels(subject)
	cli.PubSubNumSub(subject, subject1)

}

// redis set 集合操作
func redisSet(cli redis.Client) {
	key := "MOD:TEN:HASH"
	key1 := "MOD:TET:HASH"
	// 向集合添加一个或多个成员
	cli.SAdd(key, "name")
	// 获取集合的成员数
	cli.SCard(key)
	// 返回第一个集合与其他集合之间的差异。
	cli.SDiff(key, key1)
	// 返回给定所有集合的差集并存储在 destination 中
	cli.SDiffStore(key)
	// 返回给定所有集合的交集
	cli.SInter(key)
	// 返回给定所有集合的交集并存储在 destination 中
	cli.SInterStore(key, key1)
	// 判断 member 元素是否是集合 key 的成员
	cli.SIsMember(key, 10)
	// 返回集合中的所有成员
	cli.SMembers(key)
	// 将 member 元素从 source 集合移动到 destination 集合
	cli.SMove(key, key1, 10)
	// 移除并返回集合中的一个随机元素
	cli.SPop(key)
	// 返回集合中一个或多个随机数
	cli.SRandMember(key)
	// 移除集合中一个或多个成员
	cli.SRem(key, 10)
	// 返回所有给定集合的并集
	cli.SUnion(key)
	// 所有给定集合的并集存储在 destination 集合中
	cli.SUnionStore(key)
	// 迭代集合中的元素
	cli.SScan(key, 0, "*", 10)
}

// redis 有序集合
func redisSorttedSet(cli redis.Client) {
	//key := "MOD:TEN:HASH"
	//key1 := "MOD:TET:HASH"
	//// 向有序集合添加一个或多个成员，或者更新已存在成员的分数
	//cli.ZAdd(key)
	//// 获取有序集合的成员数
	//cli.ZCard(key)
	//// 计算在有序集合中指定区间分数的成员数
	//cli.ZCount(key, "5", "10")
	//// 有序集合中对指定成员的分数加上增量 increment
	//cli.ZIncrBy(key,5, "hello")
	// 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中
	//cli.ZInterStore("hello","", key)
	//// 在有序集合中计算指定字典区间内成员数量
	//cli.ZLexCount(key,"5", "10")
	//// 通过索引区间返回有序集合指定区间内的成员
	//cli.ZRange(key,1, 10)
	//// 通过字典区间返回有序集合的成员
	//cli.ZRangeByLex(key,"s")
	//// 通过分数返回有序集合指定区间内的成员
	//cli.ZRangeByScore(key, "s")
	//// 返回有序集合中指定成员的索引
	//cli.ZRank(key,"hello")
	//// 移除有序集合中的一个或多个成员
	//cli.ZRem(key)
	//// 移除有序集合中给定的字典区间的所有成员
	//cli.ZRemRangeByLex(key, "5","10")
	//// 移除有序集合中给定的排名区间的所有成员
	//cli.ZRemRangeByRank(key, 5,10)
	//// 移除有序集合中给定的分数区间的所有成员
	//cli.ZRemRangeByScore(key,"5","10")
	//// 返回有序集中指定区间内的成员，通过索引，分数从高到低
	//cli.ZRevRange(key,5,10)
	//// 返回有序集合中指定成员的排名，有序集成员按分数值递减(从大到小)排序
	//cli.ZRevRank(key, "hello")
	//// 返回有序集中，成员的分数值
	//cli.ZScore(key, "hello")
	//// 计算给定的一个或多个有序集的并集，并存储在新的 key 中
	//cli.ZUnionStore(key,"s", key1)
	//// 迭代有序集合中的元素（包括元素成员和元素分值）
	//cli.ZScore(key,"hello")

}

//获取某个用户的所有session
func GetUserSessionNew(cli redis.Client, strUid string) {

	match := "CHAT:SESSION:HASH:" + "*:" + strUid + ":*"
	ssidMap := make(map[string]int)
	ssidArr := make([]string, 0)
	cursor := uint64(0)

	for {
		ret := cli.Scan(cursor, match, 1000)
		if ret.Err() != nil {
			break
		}
		tmKeys, tmCur := ret.Val()
		for _, v := range tmKeys {
			if _, has := ssidMap[v]; !has {
				ssidMap[v] = 0
				if len(v) > len("CHAT:SESSION:HASH:") {
					ssidArr = append(ssidArr, v[len("CHAT:SESSION:HASH:"):])
				}
			}
		}

		if tmCur <= 0 {
			break
		}
		cursor = tmCur
	}
	return
}
